<!-- 固钉容器 -->
<template>
  <div class="m-affix-position"
       :style="renderStyle">
    <slot name="content"></slot>
  </div>
</template>

<script lang="ts" setup>
/** JS */
import {defineProps, withDefaults, ref, reactive, watch, nextTick, onMounted, onUnmounted} from 'vue';

/** props */
interface AffixPanelProps {
  // 距离页面的高度
  top: string,
  // 距离页面的右距离
  right: string,
  // 距离页面的下距离
  bottom: string,
  // 距离页面的左距离
  left: string,
  // 组件高度
  height: string,
  // 组件宽度
  width: string,
  // 滚动后距离目标 dom 底部的高度
  end: string,
  // 页面 z-index
  zIndex: number,
  // 指定监听的 dom 元素
  dom?: any
}

/** 钩子参数默认值 */
const props = withDefaults(defineProps<AffixPanelProps>(), {
  top: '0px',
  right: '0px',
  bottom: '0px',
  left: '0px',
  end: '0px',
  height: '350px',
  width: '200px',
  zIndex: 10,
  dom: document.body
});

let renderValue = reactive({
  top: props.top,
  right: props.right,
  bottom: props.bottom,
  left: props.left,
  end: props.end,
  dom: props.dom
});
let renderStyle = ref(''), saveTop = ref('');
let renderStyleHandler = (n) => {
  let json = JSON.parse(JSON.stringify(n));
  let style = '';
  if (json.top === '0px' && json.bottom === '0px' && json.left === '0px' && json.right === '0px') {
    style = `top: 0px; right: 0px; bottom: 0px; left: 0px;`;
  } else {
    if (json.top !== '0px' && json.bottom === '0px') {
      style += `top: ${json.top};`;
    } else if (json.top === '0px' && json.bottom !== '0px') {
      style += `bottom: ${json.bottom};`;
    }
    if (json.left !== '0px' && json.right === '0px') {
      style += `left: ${json.left};`;
    } else if (json.left === '0px' && json.right !== '0px') {
      style += `right: ${json.right};`;
    }

    if (json.top !== '0px' && json.bottom !== '0px' && json.left !== '0px' && json.right !== '0px') {
      style = `top: ${json.top}; right: ${json.right}; bottom: ${json.bottom}; left: ${json.left};`;
    }
  }
  style += `z-index: ${props.zIndex};`;
  // console.log(style)
  return style;
}

renderStyle.value = renderStyleHandler(renderValue);
const handler = () => {
  let dom: HTMLElement = renderValue.dom, end = renderValue.end ? Number(renderValue.end.trim().replace('px', '')) : 0;
  const defaultTop = Number(props.top.replace('px', ''));
  let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
  if (scrollTop + end <= dom.scrollHeight) {
    renderValue.top = scrollTop + defaultTop + 'px';
    saveTop.value = renderValue.top;
  }
}
watch(props, (n) => {
  // console.log(saveTop.value)
  // 更新数据
  renderValue.top = n.top;
  renderValue.right = n.right;
  renderValue.bottom = n.bottom;
  renderValue.left = n.left;
  renderValue.dom = n.dom;
  renderStyle.value = renderStyleHandler(renderValue);
  if (n.dom) {
    // 如果数据进行了更新，那么先移除原来的 scroll 事件
    window.removeEventListener('scroll', handler, true);
    window.addEventListener('scroll', handler, true);
  }
});

watch(renderValue, (n) => {
  if (saveTop.value !== '' && saveTop.value !== '0px') {
    renderValue.top = saveTop.value;
  }
  renderStyle.value = renderStyleHandler(n);
})

onMounted(() => {
  window.addEventListener('scroll', handler, true);
});
onUnmounted(() => {
  window.removeEventListener('scroll', handler, true);
})
</script>

<style lang="less" scoped>
.m-affix-position {
  position: absolute;
}
</style>
